using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using GameKit.Core;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;

namespace GameKitUnity.Core
{
    public class AssetLoader : IAssetLoader
    {
        public static AssetLoader Instance { get; } = new();
        public const string DefaultContext = "Default";
        private readonly Dictionary<string, List<AsyncOperationHandle>> _assetHandles = new();
        private readonly Dictionary<string, List<AsyncOperationHandle>> _sceneHandles = new();
        private string _currentContext = DefaultContext;

        private AssetLoader()
        {
        }

        public void SetContextAndRelease(string context)
        {
            Release(_currentContext);
            _currentContext = context;
        }

        private AsyncOperationHandle<T> Internal_LoadAsync<T>(object target, string path, Action<T> completed = null)
        {
            var context = _currentContext; // 捕获发起加载时的上下文
            var asyncOperationHandle = Addressables.LoadAssetAsync<T>(path);
            asyncOperationHandle.Completed += handle =>
            {
                if (handle.Status == AsyncOperationStatus.Succeeded)
                {
                    if (target is MonoBehaviour monoBehaviour)
                    {
                        if (monoBehaviour.gameObject == null)
                        {
                            Log.Info($"Load asset: {path} failed");
                            completed?.Invoke(default);
                        }
                        else
                        {
                            Log.Info($"Load asset: {path} completed on context: {context}");
                            completed?.Invoke(handle.Result);
                        }
                    }
                }
                else
                {
                    Log.Info($"Load asset: {path} completed on context: {context}");
                    completed?.Invoke(handle.Result);
                }

                if (!_assetHandles.ContainsKey(context))
                {
                    _assetHandles[context] = new List<AsyncOperationHandle>();
                }

                _assetHandles[context].Add(handle);
            };
            return asyncOperationHandle;
        }


        public Task<T> LoadAsync<T>(object target, string path)
        {
            return Internal_LoadAsync<T>(target, path).Task;
        }

        public Task<T> LoadAsync<T>(object target, AssetReference assetReference)
        {
            return LoadAsync<T>(target, assetReference.AssetGUID);
        }

        public void Load<T>(object target, string path, Action<T> completed)
        {
            Internal_LoadAsync(target, path, completed);
        }

        public void Load<T>(object target, AssetReference assetReference, Action<T> completed)
        {
            Load(target, assetReference.AssetGUID, completed);
        }

        public void LoadScene(string path)
        {
            var context = _currentContext;
            var asyncOperationHandle = Addressables.LoadSceneAsync(path);
            asyncOperationHandle.Completed += handle =>
            {
                if (handle.Status == AsyncOperationStatus.Succeeded)
                {
                    Log.Info($"Load scene: {path} completed on context: {context}");
                }
                else
                {
                    Log.Info($"Load scene: {path} failed");
                }

                if (!_sceneHandles.ContainsKey(context))
                {
                    _sceneHandles[context] = new List<AsyncOperationHandle>();
                }

                _sceneHandles[context].Add(handle);
            };
        }

        private void Release(string context)
        {
            if (_assetHandles.TryGetValue(context, out var assetHandles))
            {
                foreach (var handle in assetHandles)
                {
                    if (handle.IsValid())
                    {
                        Addressables.Release(handle);
                    }
                }

                assetHandles.Clear();
            }

            if (_sceneHandles.TryGetValue(context, out var sceneHandles))
            {
                foreach (var handle in sceneHandles)
                {
                    if (handle.IsValid())
                    {
                        Addressables.UnloadSceneAsync(handle);
                    }
                }

                sceneHandles.Clear();
            }
        }
    }
}